package com.mewp.permissions.system.filter;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.mewp.permissions.common.entity.Result;
import com.mewp.permissions.common.entity.ResultCodeEnum;
import com.mewp.permissions.common.utils.IpUtil;
import com.mewp.permissions.common.utils.JwtHelper;
import com.mewp.permissions.common.utils.ResponseUtil;
import com.mewp.permissions.model.po.SysUser;
import com.mewp.permissions.model.vo.LoginVo;
import com.mewp.permissions.system.config.CustomUser;
import com.mewp.permissions.system.service.AsyncLoginLogService;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import javax.servlet.FilterChain;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * 登录过滤器（对用户名和密码进行登录校验）
 *
 * @author mewp
 * @version 1.0
 * @date 2023/8/4 00:31
 */
public class TokenLoginFilter extends UsernamePasswordAuthenticationFilter {
    private final RedisTemplate<String, Object> redisTemplate;

    private final AsyncLoginLogService asyncLoginLogService;

    public TokenLoginFilter(AuthenticationManager authenticationManager,
                            RedisTemplate<String, Object> redisTemplate,
                            AsyncLoginLogService asyncLoginLogService) {
        this.setAuthenticationManager(authenticationManager);
        this.redisTemplate = redisTemplate;
        this.asyncLoginLogService = asyncLoginLogService;
        this.setPostOnly(false);
        //指定登录接口及提交方式，可以指定任意路径
        this.setRequiresAuthenticationRequestMatcher(
                new AntPathRequestMatcher("/admin/system/index/login", "POST"));
    }

    /**
     * 登录认证
     *
     * @param request  request
     * @param response response
     * @return authentication
     * @throws AuthenticationException 认证异常
     */
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        try {
            LoginVo loginVo = new ObjectMapper().readValue(request.getInputStream(), LoginVo.class);
            Authentication authenticationToken = new UsernamePasswordAuthenticationToken(loginVo.getUsername(), loginVo.getPassword());
            return this.getAuthenticationManager().authenticate(authenticationToken);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 登录成功
     *
     * @param request    request
     * @param response   response
     * @param chain      chain
     * @param authResult the object returned from the <tt>attemptAuthentication</tt>
     *                   method.
     * @throws IOException IO异常
     */
    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
                                            Authentication authResult) throws IOException {
        //获取认证对象
        CustomUser customUser = (CustomUser) authResult.getPrincipal();
        SysUser sysUser = customUser.getSysUser();
        //生成token
        String token = JwtHelper.createToken(sysUser.getId(), sysUser.getUsername());

        //记录日志
        asyncLoginLogService.recordLoginLog(customUser.getUsername(), 1, IpUtil.getIpAddress(request), "登录成功");

        //保存权限数据
        redisTemplate.opsForValue().set(sysUser.getUsername(),
                new ObjectMapper().writeValueAsString(customUser.getAuthorities()));

        Map<String, Object> result = new HashMap<>();
        result.put("token", token);
        ResponseUtil.out(response, Result.ok(result));
    }

    /**
     * 登录失败
     *
     * @param request  request
     * @param response response
     * @param failed   失败异常
     */
    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
                                              AuthenticationException failed) {
        if (failed != null) {
            ResponseUtil.out(response, Result.build(null, HttpStatus.UNAUTHORIZED.value(), failed.getMessage()));
        } else {
            ResponseUtil.out(response, Result.build(null, ResultCodeEnum.SERVICE_ERROR));
        }
    }
}
